Next: Defining Simple Commands, Previous: Lisp Definitions, Up: Lisp Definitions [Contents][Index]
The defmath function (actually a Lisp macro) is
like defun except that code in the body of the
definition can make use of the full range of Calculator data
types. The prefix ‘calcFunc-’ is added
to the specified name to get the actual Lisp function name. As a
simple example,
(defmath myfact (n)
(if (> n 0)
(* n (myfact (1- n)))
1))
This actually expands to the code,
(defun calcFunc-myfact (n)
(if (math-posp n)
(math-mul n (calcFunc-myfact (math-add n -1)))
1))
This function can be used in algebraic expressions, e.g., ‘myfact(5)’.
The ‘myfact’ function as it is
defined above has the bug that an expression
‘myfact(a+b)’ will be simplified to 1
because the formula ‘a+b’ is not
considered to be posp. A robust factorial function
would be written along the following lines:
(defmath myfact (n)
(if (> n 0)
(* n (myfact (1- n)))
(if (= n 0)
1
nil))) ; this could be simplified as: (and (= n 0) 1)
If a function returns nil, it is left
unsimplified by the Calculator (except that its arguments will be
simplified). Thus, ‘myfact(a+1+2)’ will
be simplified to ‘myfact(a+3)’ but no
further. Beware that every time the Calculator reexamines this
formula it will attempt to resimplify it, so your function ought
to detect the returning-nil case as efficiently as
possible.
The following standard Lisp functions are treated by
defmath: +, -,
*, /, %, ^ or
expt, =, <,
>, <=, >=,
/=, 1+, 1-,
logand, logior, logxor,
logandc2, lognot. Also, ~=
is an abbreviation for math-nearly-equal, which is
useful in implementing Taylor series.
For other functions func, if a function by the name ‘calcFunc-func’ exists it is used, otherwise if a function by the name ‘math-func’ exists it is used, otherwise if func itself is defined as a function it is used, otherwise ‘calcFunc-func’ is used on the assumption that this is a to-be-defined math function. Also, if the function name is quoted as in ‘('integerp a)’ the function name is always used exactly as written (but not quoted).
Variable names have ‘var-’ prepended
to them unless they appear in the function’s argument list
or in an enclosing let, let*,
for, or foreach form, or their names
already contain a ‘-’ character. Thus a
reference to ‘foo’ is the same as a
reference to ‘var-foo’.
A few other Lisp extensions are available in
defmath definitions:
elt function accepts any number of index
variables. Note that Calc vectors are stored as Lisp lists
whose first element is the symbol vec; thus,
‘(elt v 2)’ yields the second element
of vector v, and ‘(elt m i
j)’ yields one element of a Calc matrix.setq function has been extended to act
like the Common Lisp setf function. (The name
setf is recognized as a synonym of
setq.) Specifically, the first argument of
setq can be an nth, elt,
car, or cdr form, in which case the
effect is to store into the specified element of a list. Thus,
‘(setq (elt m i j) x)’ stores
‘x’ into one element of a matrix.for looping construct is available. For
example, ‘(for ((i 0 10)) body)’
executes body once for each binding of
‘i’ from zero to 10. This is like a
let form in that ‘i’ is
temporarily bound to the loop count without disturbing its
value outside the for construct. Nested loops, as
in ‘(for ((i 0 10) (j 0 (1- i) 2))
body)’, are also available. For each value of
‘i’ from zero to 10,
‘j’ counts from 0 to
‘i-1’ in steps of two. Note that
for has the same general outline as
let*, except that each element of the header is a
list of three or four things, not just two.foreach construct loops over elements of a
list. For example, ‘(foreach ((x (cdr v)))
body)’ executes body with
‘x’ bound to each element of Calc
vector ‘v’ in turn. The purpose of
cdr here is to skip over the initial
vec symbol in the vector.break function breaks out of the innermost
enclosing while, for, or
foreach loop. If given a value, as in
‘(break x)’, this value is returned by
the loop. (Lisp loops otherwise always return
nil.)return function prematurely returns from
the enclosing function. For example, ‘(return (+ x
y))’ returns ‘x+y’ as the
value of a function. You can use return anywhere
inside the body of the function.Non-integer numbers (and extremely large integers) cannot be
included directly into a defmath definition. This is
because the Lisp reader will fail to parse them long before
defmath ever gets control. Instead, use the
notation, ‘:"3.1415"’. In fact, any
algebraic formula can go between the quotes. For example,
(defmath sqexp (x) ; sqexp(x) == sqrt(exp(x)) == exp(x*0.5)
(and (numberp x)
(exp :"x * 0.5")))
expands to
(defun calcFunc-sqexp (x)
(and (math-numberp x)
(calcFunc-exp (math-mul x '(float 5 -1)))))
Note the use of numberp as a guard to ensure that
the argument is a number first, returning nil if
not. The exponential function could itself have been included in
the expression, if we had preferred: ‘:"exp(x *
0.5)"’. As another example, the
multiplication-and-recursion step of myfact could
have been written
:"n * myfact(n-1)"
A good place to put your defmath commands is your
Calc init file (the file given by
calc-settings-file, typically
~/.emacs.d/calc.el), which will not be loaded until
Calc starts. If a file named .emacs exists in your
home directory, Emacs reads and executes the Lisp forms in this
file as it starts up. While it may seem reasonable to put your
favorite defmath commands there, this has the
unfortunate side-effect that parts of the Calculator must be
loaded in to process the defmath commands whether or
not you will actually use the Calculator! If you want to put the
defmath commands there (for example, if you redefine
calc-settings-file to be .emacs), a
better effect can be had by writing
(put 'calc-define 'thing '(progn (defmath ... ) (defmath ... ) ))
The put function adds a property to a
symbol. Each Lisp symbol has a list of properties associated with
it. Here we add a property with a name of thing and
a ‘(progn ...)’ form as its value. When
Calc starts up, and at the start of every Calc command, the
property list for the symbol calc-define is checked
and the values of any properties found are evaluated as Lisp
forms. The properties are removed as they are evaluated. The
property names (like thing) are not used; you should
choose something like the name of your project so as not to
conflict with other properties.
The net effect is that you can put the above code in your .emacs file and it will not be executed until Calc is loaded. Or, you can put that same code in another file which you load by hand either before or after Calc itself is loaded.
The properties of calc-define are evaluated in
the same order that they were added. They can assume that the
Calc modules calc.el, calc-ext.el, and
calc-macs.el have been fully loaded, and that the
*Calculator* buffer will be the current buffer.
If your calc-define property only defines
algebraic functions, you can be sure that it will have been
evaluated before Calc tries to call your function, even if the
file defining the property is loaded after Calc is loaded. But if
the property defines commands or key sequences, it may not be
evaluated soon enough. (Suppose it defines the new command
tweak-calc; the user can load your file, then type
M-x tweak-calc before Calc has had chance to do
anything.) To protect against this situation, you can put
(run-hooks 'calc-check-defines)
at the end of your file. The calc-check-defines
function is what looks for and evaluates properties on
calc-define; run-hooks has the
advantage that it is quietly ignored if
calc-check-defines is not yet defined because Calc
has not yet been loaded.
Examples of things that ought to be enclosed in a
calc-define property are defmath calls,
define-key calls that modify the Calc key map, and
any calls that redefine things defined inside Calc. Ordinary
defuns need not be enclosed with
calc-define.
Next: Defining Simple Commands, Previous: Lisp Definitions, Up: Lisp Definitions [Contents][Index]